package vn.support.connection.factory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import vn.support.core.annotation.XML;

/**
 * @author nttuyen
 * @version 0.0.1
 */
public class DataObjectFactory implements EntityFactory {

	public static final String ROOT_ELEMENT_NAME = "swiftwinapp";
    private static final Logger log = Logger.getLogger(DataObjectFactory.class);
	
	@SuppressWarnings("unchecked")
	public <T> T create(Element element, Class<T> c) throws Exception {
		if(c.isPrimitive() || element.isTextOnly()) {
			return this.parseString(c, element.getTextTrim());
		}
		
		T instance = c.newInstance();
		
		Field[] fields = c.getDeclaredFields();
		for(Field field : fields) {
			field.setAccessible(true);
			String path = field.getName().toLowerCase();
			XML xmlAnnotation = field.getAnnotation(XML.class);
			if(xmlAnnotation != null && !"".equals(xmlAnnotation.name())) {
				path = xmlAnnotation.name();
			}
			
			boolean isCollection = false;
			Class<?> fieldType = field.getType();
			
			//TODO: Cho nay phai xu ly khi la List, khi la Set,..
			@SuppressWarnings("rawtypes")
			Collection fieldValue = new LinkedList();
			
			if(fieldType.equals(Collection.class)) {
				if(xmlAnnotation == null || xmlAnnotation.type() == null || Object.class.equals(xmlAnnotation.type())) {
					//TODO: Cho nay xu the nao day
					throw new Exception("Type in collection is need!");
				}
				isCollection = true;
				fieldType = xmlAnnotation.type();
			} else {
				for(Class<?> interfaces : field.getType().getInterfaces()){
					if(interfaces.equals(Collection.class)) {
						if(xmlAnnotation == null || xmlAnnotation.type() == null || Object.class.equals(xmlAnnotation.type())) {
							//TODO: Cho nay xu the nao day
							throw new Exception("Type in collection is need!");
						}
						isCollection = true;
						fieldType = xmlAnnotation.type();
						break;
					}
				}
			}
			
			if(fieldType.isPrimitive()) {
				fieldType = this.getWraperClassForPrimaryType(fieldType);
			}
			
			List<Element> fieldElements = element.selectNodes(path);
			for(Element e : fieldElements) {
				Object obj = this.create(e, fieldType);
				if(!isCollection) {
					field.set(instance, obj);
					break;
				}
				fieldValue.add(obj);
			}
			if(isCollection) {
				field.set(instance, fieldValue);
			}
		}
		return instance;
	}
	
	public <T> Collection<T> create(String xml, String path, Class<T> c) {
		try {
			List<T> list = new ArrayList<T>();
			Document document = DocumentHelper.parseText(xml);
			@SuppressWarnings("unchecked")
			List<Element> elements = document.selectNodes(path);
			for (Element element : elements) {
				T object = create(element, c);
				list.add(object);
			}			
			return list;
		} catch (Exception e) {
			log.error("DataObjectFactory error", e);
			return Collections.emptyList();
		}
	}
	
	private Class<?> getWraperClassForPrimaryType(Class<?> c) {
		if(c == null || !c.isPrimitive()) return Object.class;
		
		if(c.equals(Short.TYPE)) return Short.class;
		if(c.equals(Integer.TYPE)) return Integer.class;
		if(c.equals(Long.TYPE)) return Long.class;
		if(c.equals(Double.TYPE)) return Double.class;
		if(c.equals(Float.TYPE)) return Float.class;
		if(c.equals(Boolean.TYPE)) return Boolean.class;
		if(c.equals(Character.TYPE)) return Character.class;
		if(c.equals(Byte.TYPE)) return Byte.class;
		if(c.equals(Void.TYPE)) return Void.class;
		
		return Object.class;
	}
	
	private <T> T parseString(Class<T> c, String value) {
		if(c == Short.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Short.parseShort(value));
		}
		if(c == Integer.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Integer.parseInt(value));
		}
		if(c == Long.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Long.parseLong(value));
		}
		if(c == Float.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Float.parseFloat(value));
		}
		if(c == Double.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Double.parseDouble(value));
		}
		if(c == Character.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "";
			}
			return c.cast(value.length() > 0 ? value.charAt(0) : ' ');
		}
		if(c == Boolean.class) {
			if("NULL".equals(value)) {
				value = "false";
			}
			return c.cast(Boolean.parseBoolean(value));
		}
		if(c == Byte.class) {
			if("NULL".equals(value) || "false".equalsIgnoreCase(value)) {
				value = "0";
			}
			return c.cast(Byte.parseByte(value));
		}
		
		if(c.equals(String.class)) {
			if("NULL".equals(value)) {
				value = null;
			}
			return c.cast(value);
		}
		
		return null;
	}
}
